<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>call、apply</title>
</head>

<body>

</body>

</html>

<script>
  function fn1() {
    console.log(1, this);
  }

  function fn2() {
    console.log(2, this);
  }

  //  实现call
  Function.prototype.call = function (context) {
    context = context ? Object(context) : window;
    context.fn = this;
    let args = [];
    for (let i = 1; i < arguments.length; i++) {
      args.push('arguments[' + i + ']');
    }
    // 利用数组的toString的特性
    let r = eval('context.fn(' + args + ')');
    delete context.fn;
    return r;
  }

  //  实现apply
  Function.prototype.apply = function (context, args) {
    context = context ? Object(context) : window;
    context.fn = this;
    if (!args) {
      return context.fn();
    }
    // 利用数组的toString的特性
    let r = eval('context.fn(' + args + ')');
    delete context.fn;
    return r;
  }

  // call的特点
  // 1) 可以改变我们当前函数的this指向
  // 2) 还会让当前函数执行

  fn1.call('hello', 1, 2);
  fn1.call(fn2); // fn1执行，this是fn2
  fn1.call.call(fn2);  // 相当于fn2执行, this是window
  fn1.call.call.call(fn2); // 相当于fn2执行, this是window

  console.log('------------- apply -----------------');

  fn1.apply('hello', [1, 2]);
  fn1.apply(fn2); // fn1执行，this是fn2
  fn1.apply.apply(fn2);  // 相当于fn2执行, this是window
  fn1.apply.apply.apply(fn2); // 相当于fn2执行, this是window
</script>